<template>
  <div
    class="el-select"
    :class="[selectSize ? 'el-select--' + selectSize : '']"
  >
    <div
      class="el-select__tags"
      ref="tags"
      :style="{ 'max-width': inputWidth - 32 + 'px', width: '100%' }"
      @click="scrollToBottom"
    >
      <span v-if="collapseTags && selected.length">
        <el-tag
          :closable="!selectDisabled"
          :size="collapseTagSize"
          type="info"
          @close="deleteTag($event, selected[0], 0)"
          disable-transitions
        >
          <span class="el-select__tags-text">{{ selected[0] }}</span>
        </el-tag>
        <el-tag
          v-if="selected.length > 1"
          :closable="false"
          :size="collapseTagSize"
          type="info"
          disable-transitions
        >
          <span class="el-select__tags-text">+ {{ selected.length - 1 }}</span>
        </el-tag>
      </span>
      <transition-group @after-leave="resetInputHeight" v-if="!collapseTags">
        <el-tag
          v-for="(item, index) in selected"
          :key="item + index"
          :closable="!selectDisabled"
          :size="collapseTagSize"
          type="info"
          @close="deleteTag($event, item, index)"
          disable-transitions
        >
          <span class="el-select__tags-text">{{ item }}</span>
        </el-tag>
      </transition-group>

      <input
        type="text"
        class="el-select__input"
        :class="[selectSize ? `is-${selectSize}` : '']"
        :disabled="selectDisabled"
        v-if="!readonly && !isLimit && allowCreate"
        @focus="handleFocus"
        @blur="handleBlur"
        @keyup="managePlaceholder"
        @keydown="resetInputState"
        @compositionstart="handleComposition"
        @compositionupdate="handleComposition"
        @compositionend="handleComposition"
        v-model="query"
        @input="handleQueryChange"
        @paste="handleParse"
        :style="{
          'flex-grow': '1',
          width: inputLength / (inputWidth - 32) + '%',
          'max-width': inputWidth - 42 + 'px',
        }"
        ref="input"
      />
    </div>
    <el-input
      ref="reference"
      v-model="selectedLabel"
      type="text"
      :placeholder="currentPlaceholder"
      :name="name"
      :id="id"
      :size="selectSize"
      :disabled="selectDisabled"
      readonly
      :class="{ 'is-focus': softFocus }"
      @focus="handleFocus"
      @blur="handleBlur"
      @mouseenter.native="inputHovering = true"
      @mouseleave.native="inputHovering = false"
    >
      <template slot="prefix" v-if="$slots.prefix">
        <slot name="prefix"></slot>
      </template>
      <template slot="suffix">
        <i
          v-if="selected.length > 0 || query"
          class="el-select__caret el-input__icon el-icon-circle-close"
          @click="handleClearClick"
        ></i>
      </template>
    </el-input>
  </div>
</template>

<script type="text/babel">
import Emitter from "element-ui/src/mixins/emitter";
import Focus from "element-ui/src/mixins/focus";
import Locale from "element-ui/src/mixins/locale";
import {
  addResizeListener,
  removeResizeListener,
} from "element-ui/src/utils/resize-event";
import { valueEquals } from "element-ui/src/utils/util";
import { isKorean } from "element-ui/src/utils/shared";

const validators = {
  email: new RegExp(
    /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
  ),
  url: new RegExp(
    /^(https?|ftp|rmtp|mms):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i
  ),
  text: new RegExp(/^[a-zA-Z]+$/),
  digits: new RegExp(/^[\d() \.\:\-\+#]+$/),
  isodate: new RegExp(
    /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/
  ),
};

export default {
  mixins: [Emitter, Locale, Focus("reference")],

  name: "commonInputTag",

  inject: {
    elForm: {
      default: "",
    },

    elFormItem: {
      default: "",
    },
  },

  provide() {
    return {
      select: this,
    };
  },

  computed: {
    _elFormItemSize() {
      return (this.elFormItem || {}).elFormItemSize;
    },

    debounce() {
      return 0;
    },

    selectSize() {
      return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
    },

    selectDisabled() {
      return this.disabled || (this.elForm || {}).disabled;
    },

    collapseTagSize() {
      return ["small", "mini"].indexOf(this.selectSize) > -1 ? "mini" : "small";
    },

    propPlaceholder() {
      return typeof this.placeholder !== "undefined"
        ? this.placeholder
        : this.t("el.select.placeholder");
    },

    isLimit: function () {
      return this.limit > 0 && Number(this.limit) === this.value.length;
    },
  },

  props: {
    name: String,
    id: String,
    value: {
      required: true,
    },
    size: String,
    readonly: Boolean,
    disabled: Boolean,
    placeholder: {
      type: String,
      required: false,
    },
    collapseTags: Boolean,
    validate: {
      type: String | Function | Object,
      default: "",
    },
    addTagOnKeys: {
      type: Array,
      default: function () {
        return [
          13, // Return
          188, // Comma ','
          32, // kongge
          9, // Tab
        ];
      },
    },
    addTagOnBlur: {
      type: Boolean,
      default: true,
    },
    limit: {
      type: Number,
      default: -1,
    },
    allowDuplicates: {
      type: Boolean,
      default: true,
    },
    allowCreate: {
      type: Boolean,
      default: true,
    },
    beforeAdding: {
      type: Function,
    },
  },

  data() {
    return {
      createdLabel: null,
      createdSelected: false,
      selected: [],
      inputLength: 20,
      inputWidth: 0,
      initialInputHeight: 0,
      cachedPlaceHolder: "",
      softFocus: false,
      selectedLabel: "",
      query: "",
      inputHovering: false,
      currentPlaceholder: "",
      isOnComposition: false,
    };
  },

  watch: {
    selectDisabled() {
      this.$nextTick(() => {
        this.resetInputHeight();
      });
    },

    propPlaceholder(val) {
      this.cachedPlaceHolder = this.currentPlaceholder = val;
    },

    value(val, oldVal) {
      this.resetInputHeight();
      if ((val && val.length > 0) || (this.$refs.input && this.query !== "")) {
        this.currentPlaceholder = "";
      } else {
        this.currentPlaceholder = this.cachedPlaceHolder;
      }
      this.query = "";
      this.handleQueryChange(this.query);
      this.setSelected();
      if (!valueEquals(val, oldVal)) {
        this.dispatch("ElFormItem", "el.form.change", val);
      }
    },
  },

  methods: {
    handleComposition(event) {
      const text = event.target.value;
      if (event.type === "compositionend") {
        this.isOnComposition = false;
        this.$nextTick((_) => this.handleQueryChange(text));
      } else {
        const lastCharacter = text[text.length - 1] || "";
        this.isOnComposition = !isKorean(lastCharacter);
      }
    },

    handleQueryChange(val) {
      this.$nextTick(() => {
        if (this.$refs.input) {
          const length = this.$refs.input.value.length * 15 + 20;
          this.inputLength = this.collapseTags ? Math.min(50, length) : length;
        }
        this.managePlaceholder();
        this.resetInputHeight();
      });
    },

    emitChange(val) {
      if (!valueEquals(this.value, val)) {
        this.$emit("change", val);
      }
    },

    setSelected() {
      let result = [];
      if (Array.isArray(this.value)) {
        this.value.forEach((value) => {
          result.push(value);
        });
      }
      this.selected = result;
      this.$nextTick(() => {
        this.resetInputHeight();
      });
    },

    scrollToBottom(e) {
      if (e.target) {
        e.target.scrollTop = e.target.scrollHeight;
        if (this.$refs.input) {
          this.$refs.input.focus();
        }
      }
    },

    handleFocus(event) {
      if (!this.softFocus) {
        this.softFocus = true;
        if (this.$refs.tags) {
          this.$refs.tags.scrollTop = this.$refs.tags.scrollHeight;
        }
        this.$emit("focus", event);
      } else {
        this.softFocus = false;
      }
    },

    blur() {
      this.$refs.reference.blur();
    },

    handleBlur(event) {
      setTimeout(() => {
        if (this.addTagOnBlur && this.query.trim()) {
          this.addTag(this.query.trim());
        }
        this.$emit("blur", event);
      }, 50);
      this.softFocus = false;
    },

    handleParse(e) {
      if (this.readOnly || !this.allowCreate || this.isLimit) return;
      setTimeout(() => {
        let split = e.target.value.split(/\s+/);
        if (split && split.length > 1) {
          split = split.filter((one) => one.trim());
          split.forEach((one) => {
            this.addTag(one.replace(/\u2063/, ""));
          });
        }
      });
    },

    async addTag(value) {
      const tag = this.beforeAdding ? await this.beforeAdding(value) : value;

      const isValid = await this.validateIfNeeded(tag);

      if (
        tag &&
        isValid &&
        (this.allowDuplicates || this.value.indexOf(tag) === -1) &&
        !this.isLimit
      ) {
        this.value.push(tag);
      }
    },

    validateIfNeeded(tagValue) {
      if (this.validate === "" || this.validate === undefined) {
        return true;
      }

      if (typeof this.validate === "function") {
        return this.validate(tagValue);
      }

      if (
        typeof this.validate === "string" &&
        Object.keys(validators).indexOf(this.validate) > -1
      ) {
        return validators[this.validate].test(tagValue);
      }

      if (
        typeof this.validate === "object" &&
        this.validate.test !== undefined
      ) {
        return this.validate.test(tagValue);
      }

      return true;
    },

    handleClearClick(event) {
      this.query = "";
      this.deleteSelected(event);
      this.managePlaceholder();
    },

    deletePrevTag(e) {
      if (e.target.value.length <= 0 && !this.toggleLastOptionHitState()) {
        const value = this.value.slice();
        value.pop();
        this.$emit("input", value);
        this.emitChange(value);
      }
    },

    managePlaceholder() {
      if (this.value.length === 0) {
        this.currentPlaceholder = this.$refs.input?.value
          ? ""
          : this.cachedPlaceHolder;
      }
    },

    resetInputState(e) {
      if (this.addTagOnKeys.indexOf(e.keyCode) !== -1 && this.query.trim()) {
        this.addTag(this.query.trim());
      }
      this.inputLength = this.$refs.input.value.length * 15 + 20;
      this.resetInputHeight();
    },

    resetInputHeight() {
      if (this.collapseTags && !this.filterable) return;
      this.$nextTick(() => {
        if (!this.$refs.reference) return;
        let inputChildNodes = this.$refs.reference.$el.childNodes;
        let input = [].filter.call(
          inputChildNodes,
          (item) => item.tagName === "INPUT"
        )[0];
        const tags = this.$refs.tags;
        const tagsHeight = tags
          ? Math.round(tags.getBoundingClientRect().height)
          : 0;
        const sizeInMap = this.initialInputHeight || 40;
        input.style.height =
          this.selected.length === 0
            ? sizeInMap + "px"
            : Math.max(
                tags ? tagsHeight + (tagsHeight > sizeInMap ? 6 : 0) : 0,
                sizeInMap
              ) + "px";
      });
    },

    deleteSelected(event) {
      event.stopPropagation();
      const value = [];
      this.$emit("input", value);
      this.emitChange(value);
      this.$emit("clear");
    },

    deleteTag(event, tag) {
      let index = this.selected.indexOf(tag);
      if (index > -1 && !this.selectDisabled) {
        const value = this.value.slice();
        value.splice(index, 1);
        this.$emit("input", value);
        this.emitChange(value);
        this.$emit("remove-tag", tag, index);
      }
      event.stopPropagation();
    },

    resetInputWidth() {
      this.inputWidth = this.$refs.reference.$el.getBoundingClientRect().width;
    },

    handleResize() {
      this.resetInputWidth();
      this.resetInputHeight();
    },
  },

  created() {
    this.cachedPlaceHolder = this.currentPlaceholder = this.propPlaceholder;
    if (!Array.isArray(this.value)) {
      this.$emit("input", []);
    }

    this.$on("setSelected", this.setSelected);
  },

  mounted() {
    if (Array.isArray(this.value) && this.value.length > 0) {
      this.currentPlaceholder = "";
    }
    addResizeListener(this.$el, this.handleResize);

    const reference = this.$refs.reference;
    if (reference && reference.$el) {
      const sizeMap = {
        medium: 36,
        small: 32,
        mini: 28,
      };
      const input = reference.$el.querySelector("input");
      this.initialInputHeight =
        input.getBoundingClientRect().height || sizeMap[this.selectSize];
    }
    this.resetInputHeight();
    this.$nextTick(() => {
      if (reference && reference.$el) {
        this.inputWidth = reference.$el.getBoundingClientRect().width;
      }
    });
    this.setSelected();
  },

  beforeDestroy() {
    if (this.$el && this.handleResize)
      removeResizeListener(this.$el, this.handleResize);
  },
};
</script>
